﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GeneticAlgorythm.Algorythm
{
    public class PartiallyMatchedCrossover : Crossover
    {
        private PermutationChromosome _firstParentChromosome;
        private PermutationChromosome _secondParentChromosome;
        private PermutationChromosome _firstChildrenChromosome;
        private PermutationChromosome _secondChildrenChromosome;
        private int _matchSectionStart;
        private int _matchSectionEnd;
        private int _chromosomeSize;
        private bool _sectionStartAndEndAlreadySet = false;

        public PartiallyMatchedCrossover()
        {
        }

        private void verifyAndCorrectSectionStartAndEnd()
        {
            while (_matchSectionStart.Equals(_matchSectionEnd)) _matchSectionEnd = RandomGenerator.getInstance().generateRandomIntegerFromZeroTo(_chromosomeSize);

            if (_matchSectionStart > _matchSectionEnd)
                swapInts(ref _matchSectionStart, ref _matchSectionEnd);
        }

        public PartiallyMatchedCrossover(int matchSectionStart, int matchSectionEnd)
        {
            _matchSectionStart = matchSectionStart;
            _matchSectionEnd = matchSectionEnd;
            _sectionStartAndEndAlreadySet = true;
        }
        public List<Chromosome> execute(Chromosome firstParent , Chromosome secondParent)
        {
            setUps(firstParent, secondParent);

            if (!_sectionStartAndEndAlreadySet)
            {
                _matchSectionStart = RandomGenerator.getInstance().generateRandomIntegerFromZeroTo(_chromosomeSize);
                _matchSectionEnd = RandomGenerator.getInstance().generateRandomIntegerFromZeroTo(_chromosomeSize);

                verifyAndCorrectSectionStartAndEnd();
            }

            fillChildrenWithParentChromosome();

            fillMatchingSection(_matchSectionStart, _matchSectionEnd);

            int startGene = _matchSectionStart;
            int endGene = _matchSectionEnd;

            List<int> index = new List<int>();
            List<int> index2 = new List<int>();

            for (int i = ((endGene + 1) != _chromosomeSize) ? (endGene + 1) : 0; i != startGene;) 
	        {
		        for(int j = startGene; j <= endGene; ++j)
		        {
                    if (_firstChildrenChromosome.getGene(i).Equals(_firstChildrenChromosome.getGene(j)))
				        index.Add(i);

        			if(_secondChildrenChromosome.getGene(i).Equals(_secondChildrenChromosome.getGene(j)))
		        		index2.Add(i);
		        }
                if(!(i + 1).Equals(_chromosomeSize))
                    i++;
                else
                    i = 0; 
        	}

	        for(int i = 0; i < index.Count;++i)
	        {
                swapGenes(_firstChildrenChromosome, index[i], _secondChildrenChromosome, index2[i]);
	        }

            return new List<Chromosome> {_firstChildrenChromosome, _secondChildrenChromosome };
        }

        private void setUps(Chromosome firstParent, Chromosome secondParent)
        {
            _firstParentChromosome = (PermutationChromosome) firstParent;
            _secondParentChromosome = (PermutationChromosome) secondParent;

            _chromosomeSize = _firstParentChromosome.getSize();

            _firstChildrenChromosome = new PermutationChromosome(_chromosomeSize);
            _secondChildrenChromosome = new PermutationChromosome(_chromosomeSize);
        }

        private void fillMatchingSection(int matchSectionStart, int matchSectionEnd)
        {
            for (int i = matchSectionStart; i <= matchSectionEnd; i++)
            {
                _firstChildrenChromosome.setGene(i, _secondParentChromosome.getGene(i));
                _secondChildrenChromosome.setGene(i, _firstParentChromosome.getGene(i));
            }
        }

        private void fillChildrenWithParentChromosome()
        {
            for (int i = 0; i < _chromosomeSize; i++)
            {
                _firstChildrenChromosome.setGene(i, _firstParentChromosome.getGene(i));
                _secondChildrenChromosome.setGene(i, _secondParentChromosome.getGene(i));
            }
        }

        private void swapInts(ref int startGene, ref int endGene)
        {
                int tmp = endGene;
                endGene = startGene;
                startGene = tmp;
        }

        private void swapGenes(Chromosome chromosome1, int index1, Chromosome chromosome2, int index2)
        {
            Object tmp = chromosome1.getGene(index1);
            chromosome1.setGene(index1, chromosome2.getGene(index2));
            chromosome2.setGene(index2, tmp);

        }
    }
}
